GIS
Using R as GIS (reading a rainfall shapefile + Kriging, sf + leaflet + mapview + OSMscale)
Berry Boessenkool
Shapefiles
Reading shapefiles with maptools::readShapeSpatial and rgdal::readOGR is obsolete.
Instead, use sf::st_read. sf is on CRAN since oct 2016.
Main reaction when using sf: “Wow, that is fast!”
Download the shapefile or better: download the whole github course repository
rain <- sf::st_read("data/PrecBrandenburg/niederschlag.shp")
Reading layer `niederschlag' from data source `/home/berry/Dropbox/R/rhydro/presentations/data/PrecBrandenburg/niederschlag.shp' using driver `ESRI Shapefile'
converted into: POLYGON
Simple feature collection with 277 features and 1 field
geometry type: POLYGON
dimension: XY
bbox: xmin: 3250175 ymin: 5690642 xmax: 3483631 ymax: 5932731
epsg (SRID): NA
proj4string: +proj=tmerc +lat_0=0 +lon_0=15 +k=0.9996 +x_0=3500000 +y_0=0 +ellps=GRS80 +units=m +no_defs
Central points of rainfall Thiessen polygons
centroids <- sf::st_centroid(rain)
centroids <- sf::st_coordinates(centroids)
centroids <- as.data.frame(centroids)
top
Plotting, maps
Static plot:
plot(rain[,1])

Static map:
prj <- sf::st_crs(rain)$proj4string
cent_ll <- OSMscale::projectPoints(Y,X, data=centroids, to=OSMscale::pll(), from=prj)
#map_static <- OSMscale::pointsMap(y,x, cent_ll, fx=0.08, type="maptoolkit-topo", zoom=6)
#save(map_static, file="data/map_static.Rdata")
load("data/map_static.Rdata")
OSMscale::pointsMap(y,x, cent_ll, map=map_static)
Done. Now plotting...

Interactive map:
library(leaflet)
cent_ll$info <- paste0(sample(letters,nrow(cent_ll),TRUE), ", ", round(cent_ll$x,2),
", ", round(cent_ll$y,2))
leaflet(cent_ll) %>% addTiles() %>% addCircleMarkers(lng=~x, lat=~y, popup=~info)
Interactive map of shapefile:
# make sure to have installed the development version of mapview:
# devtools::install_github("environmentalinformatics-marburg/mapview", ref = "develop")
library(berryFunctions) # classify, seqPal
col <- seqPal(n=100, colors=c("red","yellow","blue"))[classify(rain$P1)$index]
mapview::mapview(rain, col.regions=col)
top
Kriging
Plot original points colored by third dimension:
pcol <- colorRampPalette(c("red","yellow","blue"))(50)
x <- centroids$X # use cent_ll$x for projected data
y <- centroids$Y
berryFunctions::colPoints(x, y, rain$P1, add=FALSE, col=pcol)

Calculate the variogram and fit a semivariance curve
library(geoR)
--------------------------------------------------------------
Analysis of Geostatistical Data
For an Introduction to geoR go to http://www.leg.ufpr.br/geoR
geoR version 1.7-5.2 (built on 2016-05-02) is now loaded
--------------------------------------------------------------
geoprec <- as.geodata(cbind(x,y,rain$P1))
vario <- variog(geoprec, max.dist=130000) # other maxdist for lat-lon data
variog: computing omnidirectional variogram
fit <- variofit(vario)
variofit: covariance model used is matern
variofit: weights used: npairs
variofit: minimisation function used: optim
initial values not provided - running the default search
variofit: searching for best initial value ... selected values:
sigmasq phi tausq kappa
initial.value "1325.81" "19999.05" "0" "0.5"
status "est" "est" "est" "fix"
loss value: 104819060.429841
plot(vario)
lines(fit)

Determine a useful resolution (keep in mind that computing time rises exponentially with grid size)
# distance to closest other point:
d <- sapply(1:length(x), function(i)
min(berryFunctions::distance(x[i], y[i], x[-i], y[-i])) )
# for lat-long data use (2017-Apr only available in github version of OSMscale)
# d <- OSMscale::maxEarthDist(y,x, data=cent_ll, fun=min)
hist(d/1000, breaks=20, main="distance to closest gauge [km]")

mean(d/1000) # 8 km
[1] 8.165713
Perform kriging on a grid with that resolution
res <- 1000 # 1 km, since stations are 8 km apart on average
grid <- expand.grid(seq(min(x),max(x),res),
seq(min(y),max(y),res))
krico <- krige.control(type.krige="OK", obj.model=fit)
#krobj <- krige.conv(geoprec, loc=grid, krige=krico)
#save(krobj, file="data/krobj.Rdata")
load("data/krobj.Rdata") # line above is too slow for recreation each time
Plot the interpolated values with image or an equivalent function (see Rclick 4.15) and add contour lines.
par(mar=c(0,3,0,3))
geoR:::image.kriging(krobj, col=pcol)
colPoints(x, y, rain$P1, col=pcol, legargs=list(horiz=F, title="Prec",y1=0.1,x1=0.9))
points(x,y)
plot(rain, col=NA, add=TRUE)

top
Discharge
River discharge time-series visualisation and extreme value statistics (animation + extremeStat)
Berry Boessenkool
Read, plot and aggregate data
Datasets from the UK National River Flow Archive http://nrfa.ceh.ac.uk/data/station/meanflow/39072
Download discharge csv or better: download the whole github course repository
Read and transform data
Q <- read.table("data/discharge39072.csv", skip=19, header=TRUE, sep=",", fill=TRUE)[,1:2]
colnames(Q) <- c("date","discharge")
Q$date <- as.Date(Q$date, format="%Y-%m-%d")
Examine data
head(Q)
str(Q)
'data.frame': 13222 obs. of 2 variables:
$ date : Date, format: "1979-07-20" "1979-07-21" ...
$ discharge: num 33.4 32.5 33.1 30.6 30.1 ...
Simple time series plot
plot(Q, type="l", col="blue")

Publication-ready graphics
png("DischargeVis.png", width=20, height=10, units="cm", res=500)
#pdf("DischargeVis.pdf", width=20/2.5, height=10/2.5) # vector graph
par(mar=c(3.5,3.5,2.5,0.2), mgp=c(2.3,0.7,0), las=1)
plot(Q, type="l", col="blue", main="NRFA: Thames\nRoyal Windsor Park",
xlab="Date", ylab="Discharge [m\U{00B3}/s]")
dev.off()
null device
1
Annual maxima, German hydrological year split at Oct 31
Q$hydyear <- as.numeric(format(Q$date+61, "%Y"))
annmax <- tapply(Q$discharge, Q$hydyear, max, na.rm=TRUE)
annmax <- annmax[-1]
hydyear <- as.numeric(names(annmax))
plot(hydyear, annmax, type="o", las=1)

Extreme value statistics
library(extremeStat)
# Loaded extremeStat 1.3.1 (2017-02-16). Package restructured since 0.6.0 (2016-12-13).
# Computing functions don't plot anymore and some are renamed. See help('extremeStat-deprecated')
dlf <- distLfit(annmax)
Note in distLfit: dat was not a vector.
distLfit execution took 0.21 seconds.
plotLfit(dlf)

plotLfit(dlf, cdf=TRUE)

dle <- distLextreme(dlf=dlf, RPs=c(5,10,50,100), gpd=FALSE)
plotLextreme(dle)

Logarithmic plot with many fitted distribution functions
plotLextreme(dle, nbest=16, log=TRUE)

Return values (discharge estimates for given return periods)
dle$returnlev
In reality, please use non-stationary EVS!
Uncertainty band for Wakeby distribution fit estimate
dle_boot <- distLexBoot(dle, n=10, nbest=1)
|+++++ | 10% ~01s
|++++++++++ | 20% ~01s
|+++++++++++++++ | 30% ~01s
|++++++++++++++++++++ | 40% ~01s
|+++++++++++++++++++++++++ | 50% ~01s
|++++++++++++++++++++++++++++++ | 60% ~00s
|+++++++++++++++++++++++++++++++++++ | 70% ~00s
|++++++++++++++++++++++++++++++++++++++++ | 80% ~00s
|+++++++++++++++++++++++++++++++++++++++++++++ | 90% ~00s
|++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 01s
plotLexBoot(dle_boot, distcol="green")

More details in the package vignette
vignette("extremeStat")
Animated movie
Download data from several discharge stations
http://nrfa.ceh.ac.uk/data/search Filter: River = Thames
http://environment.data.gov.uk/flood-monitoring/doc/reference#stations for lat-lon coordinates:
url <- "http://environment.data.gov.uk/flood-monitoring/id/stations?riverName=Thames"
json <- jsonlite::fromJSON(url)
str(json$items, max.level=1)
metajson <- json$items[,c("label","lat","long","dateOpened")]
meta <- read.table(header=T, as.is=T, sep=",", text="
name , lat, lon
Kingston , 51.415005,-0.308869
Days_Weir , 51.638206,-1.179444
Eynsham , 51.774692,-1.356854
West_Mill_Cricklade , 51.646694,-1.865536
Ewen , 51.674733,-1.9904
Royal_Windsor_Park , 51.485795,-0.589124
Reading , 51.461325,-0.967884
")
#library(leaflet)
#leaflet(meta) %>% addTiles() %>% addCircleMarkers(~lon, ~lat, popup=~name)
map_thames <- OSMscale::pointsMap(lat,lon,meta, fx=1, fy=0.2, plot=FALSE, zoom=6,
type="maptoolkit-topo", quiet=TRUE)
Read datasets
files <- dir("data", pattern="^Thames_", full=TRUE)
thames <- lapply(files, function(f) {
dis <- read.table(f, skip=19, header=TRUE, sep=",", fill=TRUE)[,1:2]
name <- readLines(f, n=5)[5]
name <- sub("station,name,Thames at ", "", name)
name <- gsub(" ", "_", name)
colnames(dis) <- c("date",name)
dis$date <- as.Date(dis$date, format="%Y-%m-%d")
dis
})
rm(files)
Merge datasets
dis <- Reduce(function(...) merge(..., all=T), thames)
Code to generate one movie slice
library(berryFunctions) # for lim0, monthAxis, textField, etc
scene <- function(i, vlcnote=TRUE, map=TRUE, cex=1.2)
{
sel <- 0:120
dis2 <- dis[i + sel, ]
stat <- colnames(dis)[-1]
col <- RColorBrewer::brewer.pal(ncol(dis)-1, name="Set1")
names(col) <- stat
plot(dis2$date,dis2[,2], type="n", ylim=lim0(300), las=1,
xaxt="n", yaxt="n", cex.lab=cex, xlab="",
ylab="Discharge [m\U{00B3}/s]", xaxs="i")
axis(2, cex.axis=cex, las=1)
Sys.setlocale("LC_TIME", "C")
monthAxis(midmonth=TRUE, format="%b\n%Y", cex=cex, mgp=c(3,3.5,0))
abline(h=1:6*100, v=dis2$date[format(dis2$date,"%d")=="01"], col=8)
for(s in stat) lines(dis2$date, dis2[,s], lwd=4, col=col[s])
xi <- seqR(sel,len=length(stat)+2)[-1]
xi <- head(xi, -1)
textField(dis2$date[xi], diag(as.matrix(dis2[xi,stat])), stat, cex=cex, col=col)
box()
if(map) berryFunctions::smallPlot(
{
OpenStreetMap::plot.OpenStreetMap(map_thames, removeMargin=FALSE)
pts <- OSMscale::projectPoints(lat,lon,meta,
to=map_thames$tiles[[1]]$projection)
points(pts, pch=3, cex=4, lwd=5, col=col)
},
mar=0, bg=NA, border=NA, x1=0, x2=0.5, y1=0.8, y2=1)
if(vlcnote) mtext("VLC: 'e': single frame forward\n'SHIFT+LEFT': few seconds back",
side=3, line=-9, outer=TRUE, adj=0.95, cex=cex)
}
par(mar=c(5,5,0.5,0.5), mgp=c(3,0.7,0))
scene(47200)

Actual movie
library(animation) ; library(pbapply)
saveVideo({par(mar=c(6,8,1,1), mgp=c(5.5,0.7,0))
dummy <- pbsapply(seq(47000, by=3, len=100), scene, cex=2); rm(dummy)
}, video.name="Qmovie.mp4", interval=0.1, ffmpeg="/usr/bin/ffmpeg",
ani.width=7*200, ani.height=5*200)
Open video within browser
top
Hydmod
Hydrological modelling with airGR
Katie Smith (Centre for Ecology & Hydrology) k.a.smith@ceh.ac.uk
This is an demonstration of how to use the airGR package of hydrological models in R, as well as how to plot interactive timeseries graphs with the dygraphs package.
First we need to load some packages
library(xts)
Loading required package: zoo
Attaching package: ‘zoo’
The following objects are masked from ‘package:base’:
as.Date, as.Date.numeric
library(dygraphs)
library(RColorBrewer)
Data
Now we’ll load in some observational flow data from the River Thames (naturalised) in England - with thanks to the National River Flow Archive: http://nrfa.ceh.ac.uk/data/search
observed_data <- read.csv("data/Qobs_390010.csv")
observed_data
observed_data$DATE <- strptime(observed_data$DATE, format = "%d/%m/%Y")
In order to plot this as an interactive dygraph we need to change it to xts format
observed_data_xts <- as.xts(observed_data$Qobs, order.by = observed_data$DATE)
dygraph(observed_data_xts, main="Naturalised Streamflow Observations for the Thames at Kingston")%>%
dyAxis("y", label="Streamflow (m3/s")%>%
dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1")[2])%>%
dyRangeSelector()
Now lets read in some precipitation data - this is from CEH-GEAR: https://data.gov.uk/dataset/gridded-estimates-of-daily-and-monthly-areal-rainfall-for-the-united-kingdom-1890-2012-ceh-gear
precip_data <- read.csv("data/rain_1961_2014_390010.csv")
precip_data
and some potential evapotranspiration data - this is from CHESS-PE:https://data.gov.uk/dataset/climate-hydrology-and-ecology-research-support-system-potential-evapotranspiration-dataset-for-1
PET_data <- read.csv("data/CHESS_PET_1961_2015_390010.csv")
PET_data
Note that our starting dates are not the same as our observational data, so we need to make a dataframe that matches the dates up. There are a lot of ways to do this. First we’ll convert them to the same date format.
precip_data$DATE <- strptime(precip_data$DATE, "%Y-%m-%d")
PET_data$DATE <- strptime(PET_data$DATE, "%Y-%m-%d")
now we’ll find the common period
first_date <- max(observed_data$DATE[1], precip_data$DATE[1], PET_data$DATE[1])
last_date <- min(observed_data$DATE[length(observed_data$DATE)], precip_data$DATE[length(precip_data$DATE)], PET_data$DATE[length(PET_data$DATE)])
and make a data frame of that length
# make an empty data frame
thames_data <- as.data.frame(matrix(NA,nrow=as.numeric((last_date-first_date)+1), ncol=4))
colnames(thames_data) <-c ("date","PET","precip","obs")
# make the date timeseries
thames_data$date <- seq(first_date, last_date, by="days")
# populate the data frame with the data
thames_data$obs <- observed_data$Qobs[which(observed_data$DATE==thames_data$date[1]):which(observed_data$DATE==thames_data$date[length(thames_data$date)])]
thames_data$precip <- precip_data$Mean_rainfall[which(precip_data$DATE==thames_data$date[1]):which(precip_data$DATE==thames_data$date[length(thames_data$date)])]
thames_data$PET <- PET_data$PET[which(PET_data$DATE==thames_data$date[1]):which(PET_data$DATE==thames_data$date[length(thames_data$date)])]
Interactive time series plot
plot the observed streamflow with the precipitation data
# convert the observed discharge to runoff (so its in the same units as the precip)
# divide by catchment area (m2) and mulitply by 86.4
thames_data$obs <- (thames_data$obs/9948.0)*86.4
thames_data_xts <- as.xts(thames_data[,3:4], order.by=thames_data$date)
# initiate the dygraph
dygraph(thames_data_xts)%>%
# define the first axis
dyAxis("obs", name = "y", label = "runoff (mm/day)",
valueRange = range(thames_data_xts[, "obs"],
na.rm = TRUE)* c(0.01, 1.59))%>%
# define the second axis
dyAxis("precip", name = "y2", label = "precip (mm/day)",
valueRange = rev(range(thames_data_xts[, "precip"],
na.rm = TRUE)* c(0.01, 2.99)))%>%
# plot the data
dySeries("obs",axis = 'y')%>%
dySeries("precip", axis = 'y2', stepPlot = TRUE,
fillGraph = TRUE)%>%
dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1")[2:3])%>%
dyRangeSelector()
Hydrological modeling
OK, enough messing with data, lets do some modelling
see this website for a good guide through the model: https://odelaigue.github.io/airGR/tutorial_1_getting_started.html
load the GR package
require(airGR, quietly=TRUE)
prepare the input data in the correct format
BasinObs <- thames_data
colnames(BasinObs) <- c('DatesR','E','P', 'Qobs')
create the InputsModel object - this defines which model we want to run, and defines the variables for the models input data
InputsModel <- CreateInputsModel(FUN_MOD = RunModel_GR4J,DatesR = BasinObs$DatesR,
Precip = BasinObs$P,PotEvap = BasinObs$E)
str(InputsModel)
List of 3
$ DatesR : POSIXlt[1:19723], format: "1961-01-01" "1961-01-02" ...
$ Precip : num [1:19723] 8.6376 4.571 2.1015 0.0201 7.1859 ...
$ PotEvap: num [1:19723] 0.367 0.353 0.412 0.32 0.612 ...
- attr(*, "class")= chr [1:3] "InputsModel" "daily" "GR"
# note NA values of precip and PET are NOT ALLOWED
create the RunOptions object - this defines options for the RunModel_GR4J function
# first define the period to run the model over
Ind_Run <- seq(which(BasinObs$DatesR=="1981-01-01"),
which(BasinObs$DatesR=="2014-12-31"))
RunOptions <- CreateRunOptions(FUN_MOD = RunModel_GR4J,
InputsModel = InputsModel,
IndPeriod_Run = Ind_Run,
IndPeriod_WarmUp = NULL)
Model warm-up period not defined -> default configuration used
The year preceding the run period is used
Model states initialisation not defined -> default configuration used
str(RunOptions)
List of 6
$ IndPeriod_WarmUp: int [1:365] 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 ...
$ IndPeriod_Run : int [1:12418] 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 ...
$ IniStates : num [1:67] 0 0 0 0 0 0 0 0 0 0 ...
$ IniResLevels : num [1:2] 0.3 0.5
$ Outputs_Cal : chr "Qsim"
$ Outputs_Sim : chr [1:16] "DatesR" "PotEvap" "Precip" "Prod" ...
- attr(*, "class")= chr [1:3] "RunOptions" "GR" "daily"
create the InputsCrit object - define the error metric (choose from RMSE, NSE, KGE or modified KGE (KGE2))
InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_NSE,
InputsModel = InputsModel,
RunOptions = RunOptions,
Qobs = BasinObs$Qobs[Ind_Run])
str(InputsCrit)
List of 5
$ BoolCrit : logi [1:12418] TRUE TRUE TRUE TRUE TRUE TRUE ...
$ Qobs : num [1:12418] 0.635 0.623 0.587 0.571 0.566 ...
$ transfo : chr ""
$ Ind_zeroes: NULL
$ epsilon : NULL
- attr(*, "class")= chr "InputsCrit"
create the CalibOptions object - choose the calibration algorithm
CalibOptions <- CreateCalibOptions(FUN_MOD = RunModel_GR4J,
FUN_CALIB = Calibration_Michel)
str(CalibOptions)
List of 3
$ FixedParam : logi [1:4] NA NA NA NA
$ SearchRanges : num [1:2, 1:4] 4.59e-05 2.18e+04 -1.09e+04 1.09e+04 4.59e-05 ...
$ StartParamDistrib: num [1:3, 1:4] 169.017 247.151 432.681 -2.376 -0.649 ...
- attr(*, "class")= chr [1:3] "CalibOptions" "GR4J" "HBAN"
run the calibration
OutputsCalib <- Calibration_Michel(InputsModel = InputsModel,
RunOptions = RunOptions,
InputsCrit = InputsCrit,
CalibOptions = CalibOptions,
FUN_MOD = RunModel_GR4J,
FUN_CRIT = ErrorCrit_NSE)
Grid-Screening in progress (0% 20% 40% 60% 80% 100%)
Screening completed (81 runs)
Param = 432.681 , -0.649 , 83.096 , 2.384
Crit NSE[Q] = 0.8923
Steepest-descent local search in progress
Calibration completed (19 iterations, 216 runs)
Param = 607.894 , -0.734 , 87.357 , 2.315
Crit NSE[Q] = 0.9246
NSE of 0.9246 - not bad at all!
define the parameters found by the calibration routine
Param <- OutputsCalib$ParamFinalR
Param
[1] 607.8936811 -0.7336304 87.3567230 2.3153153
RUN THE MODEL!
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
RunOptions = RunOptions,
Param= Param)
str(OutputsModel)
List of 16
$ DatesR : POSIXlt[1:12418], format: "1981-01-01 00:00:00" "1981-01-02 00:00:00" ...
$ PotEvap : num [1:12418] 1.347 0.38 0.795 0.57 0.454 ...
$ Precip : num [1:12418] 0.0972 0.1211 0.0523 0.1147 0.3248 ...
$ Prod : num [1:12418] 352 351 350 350 349 ...
$ AE : num [1:12418] 1.127 0.334 0.663 0.488 0.43 ...
$ Perc : num [1:12418] 0.387 0.383 0.378 0.374 0.372 ...
$ PR : num [1:12418] 0.387 0.383 0.378 0.374 0.372 ...
$ Q9 : num [1:12418] 0.355 0.35 0.345 0.341 0.338 ...
$ Q1 : num [1:12418] 0.0398 0.0393 0.0387 0.0382 0.0378 ...
$ Rout : num [1:12418] 42.2 42 41.7 41.4 41.2 ...
$ Exch : num [1:12418] -0.0592 -0.0577 -0.0564 -0.0551 -0.0539 ...
$ AExch : num [1:12418] -0.099 -0.097 -0.0951 -0.0933 -0.0917 ...
$ QR : num [1:12418] 0.599 0.578 0.559 0.542 0.526 ...
$ QD : num [1:12418] 0 0 0 0 0 ...
$ Qsim : num [1:12418] 0.599 0.578 0.559 0.542 0.526 ...
$ StateEnd: num [1:67] 374.4 45.5 NA NA NA ...
- attr(*, "class")= chr [1:3] "OutputsModel" "daily" "GR"
use the inbuilt plot function to look at the results
plot(OutputsModel, Qobs=BasinObs$Qobs[Ind_Run])

looking good - but we’ve got some discrepancy at the low flows end. NSE is notorious for this, it is based on the square of the flows, so over-weights the calibration to the high flows. I wonder if the modified KGE can do any better?
# make a few changes to the calibration criteria
InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_KGE2,
InputsModel = InputsModel,
RunOptions = RunOptions,
Qobs = BasinObs$Qobs[Ind_Run])
# rerun the calibration
OutputsCalib <- Calibration_Michel(InputsModel = InputsModel,
RunOptions = RunOptions,
InputsCrit = InputsCrit,
CalibOptions = CalibOptions,
FUN_MOD = RunModel_GR4J,
FUN_CRIT = ErrorCrit_KGE2)
Grid-Screening in progress (0% 20% 40% 60% 80% 100%)
Screening completed (81 runs)
Param = 432.681 , -0.649 , 83.096 , 2.384
Crit KGE'[Q] = 0.8589
Steepest-descent local search in progress
Calibration completed (51 iterations, 502 runs)
Param = 598.748 , -0.690 , 93.679 , 2.297
Crit KGE'[Q] = 0.9621
# redefine the parameters
Param <- OutputsCalib$ParamFinalR
# rerun the model
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
RunOptions = RunOptions,
Param= Param)
# plot again
plot(OutputsModel, Qobs=BasinObs$Qobs[Ind_Run])

not much different. Oh well, we can be happy with either of those metric scores. - pause for thought - which parameter set would you choose to use?!
Validation
Let’s do some validation
# go back to the beginning, redefine the period to run on (the period we haven't used for calibration, minus the first year needed for warm up)
Ind_Run <- seq(which(BasinObs$DatesR=="1962-01-01"),
which(BasinObs$DatesR=="1980-12-31"))
RunOptions <- CreateRunOptions(FUN_MOD = RunModel_GR4J,
InputsModel = InputsModel,
IndPeriod_Run = Ind_Run,
IndPeriod_WarmUp = NULL)
Model warm-up period not defined -> default configuration used
The year preceding the run period is used
Model states initialisation not defined -> default configuration used
InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_KGE2,
InputsModel = InputsModel,
RunOptions = RunOptions,
Qobs = BasinObs$Qobs[Ind_Run])
Param <- OutputsCalib$ParamFinalR
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
RunOptions = RunOptions,
Param= Param)
OutputsCrit <- ErrorCrit_KGE2(InputsCrit = InputsCrit,
OutputsModel = OutputsModel)
Crit. KGE'[Q] = 0.9039
SubCrit. KGE'[Q] cor(sim, obs, "pearson") = 0.9466
SubCrit. KGE'[Q] sd(sim)/sd(obs) = 0.9253
SubCrit. KGE'[Q] mean(sim)/mean(obs) = 1.0282
slightly worse than the calibration period (0.9621) but not bad at all
finally, let’s run the model for the whole time period and plot a dygraph so we can look at the timeseries in more detail
Ind_Run <- seq(which(BasinObs$DatesR=="1962-01-01"),
which(BasinObs$DatesR=="2014-12-31"))
RunOptions <- CreateRunOptions(FUN_MOD = RunModel_GR4J,
InputsModel = InputsModel,
IndPeriod_Run = Ind_Run,
IndPeriod_WarmUp = NULL)
Model warm-up period not defined -> default configuration used
The year preceding the run period is used
Model states initialisation not defined -> default configuration used
Param <- OutputsCalib$ParamFinalR
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
RunOptions = RunOptions,
Param= Param)
plot_output_data <- as.data.frame(matrix(NA, ncol = 3,
nrow = length(OutputsModel$DatesR)))
colnames(plot_output_data) <- c("Date", "Qsim", "Qobs")
plot_output_data$Date <- OutputsModel$DatesR
plot_output_data$Qsim <- OutputsModel$Qsim
plot_output_data$Qobs <- BasinObs$Qobs[Ind_Run]
plot_output_data_xts <- as.xts(plot_output_data, order.by = plot_output_data$Date)
dygraph(plot_output_data_xts, main="Observed and Simulated Runoff for the Thames at Kingston (Naturalised)")%>%
dyOptions(colors = RColorBrewer::brewer.pal(3,"Set1")[2:1])%>%
dyAxis("y", label="Runoff (mm/day)")%>%
dyRangeSelector()
Thanks for listening! Hope you get to try it out.
Any general questions? Please feel free to post them on facebook page: https://www.facebook.com/groups/1130214777123909/?ref=bookmarks
GR specific questions? Email airGR@irstea.fr
top
LS0tCnRpdGxlOiAiUmh5ZHJvIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmlmIChrbml0cjo6OmlzX2h0bWxfb3V0cHV0KCkpIHsKICBrbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbCA9IEZBTFNFKQp9CmBgYAoKIyBVc2luZyBSIGluIGh5ZHJvbG9neSAoRUdVMjAxNyBzaG9ydCBjb3Vyc2UpCgoqSW5zdHJ1Y3RvcnMqOiBTaGF1biBIYXJyaWdhbiwgS2F0aWUgU21pdGgsIEJlcnJ5IEJvZXNzZW5rb29sIGFuZCBEYW5pZWwgS2xvdHogIAoqT3JnYW5pemVyKjogQmVycnkgQm9lc3Nlbmtvb2wsIFBoRCBzdHVkZW50IGF0IFBvdHNkYW0gVW5pdmVyc2l0eSAoR2VybWFueSkgIAoqQ29udGFjdCo6IFF1ZXN0aW9ucyBhbmQgZmVlZGJhY2sgYXJlIHdlbGNvbWUgdmlhIDxiZXJyeS1iQGdteC5kZT4KClRoZXNlIHNsaWRlcyBhbmQgYWxsIG90aGVyIGNvdXJzZSBtYXRlcmlhbHMgY2FuIGJlIGZvdW5kIGF0ICAKPGZvbnQgc2l6ZT0iNiI+W2dpdGh1Yi5jb20vYnJyeS9yaHlkcm9dKGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRybyk8L2ZvbnQ+ICAgCltEb3dubG9hZCB0aGUgZ2l0aHViIGNvdXJzZSByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8vYXJjaGl2ZS9tYXN0ZXIuemlwKQp3aXRoIGFsbCB0aGUgbWF0ZXJpYWxzIGluY2x1ZGluZyB0aGUgZGF0YXNldHMgYW5kIHByZXNlbnRhdGlvbiBzb3VyY2UgY29kZS4gIApUaGlzIGlzIGFuIFtSIE1hcmtkb3duIE5vdGVib29rXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL3Jfbm90ZWJvb2tzLmh0bWwpLiAgCkZvciBkaXNjdXNzaW9ucywgcGxlYXNlIHZpc2l0IHRoZSAKW0h5ZHJvbG9neSBpbiBSIEZhY2Vib29rIGdyb3VwXShodHRwczovL3d3dy5mYWNlYm9vay5jb20vZ3JvdXBzLzExMzAyMTQ3NzcxMjM5MDkvKS4gIApCZWZvcmUgcnVubmluZyB0aGUgY29kZSBibG9ja3MgYmVsb3csIHdlIHN1Z2dlc3QgdG8gZ2V0IHBhY2thZ2UgaW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucyBieSBydW5uaW5nOgpgYGBSCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Jycnkvcmh5ZHJvL21hc3Rlci9jaGVja3BjLlIiKQpgYGAKClwKCioqQWltIGFuZCBjb250ZW50cyBvZiB0aGlzIHdvcmtzaG9wKioKCldlIHdhbnQgdG86ICAKCiogU2hvdyBvZmYgaG93IGF3ZXNvbWUgUiBpcyBmb3IgaHlkcm9sb2d5IChpdCdzIFItc29tZSFeXikgIAoqIENvbnZpbmNlIHlvdSB0byBzdGFydCBvciBjb250aW51ZSB1c2luZyBSICAKKiBQcm92aWRlIGFsbCB0aGUgY29kZSBmb3IgeW91IGFzIGEgc3RhcnRpbmcgcG9pbnQKCldlIGNhbiBub3Q6ICAKCiogVGVhY2ggeW91IGFjdHVhbCBSIGNvZGluZyAoOTAgbWlucyBpcyB0b28gc2hvcnQgZm9yIGEgdHV0b3JpYWwpCgpXZSBoYXZlIHByZXBhcmVkOgoKKiBbR29vZCBjb2RpbmcgcHJhY3RpY2UsIHJlcG9ydCBnZW5lcmF0aW9uXSgjcmVwb3J0KSAoUnN0dWRpbywgYHJtYXJrZG93bmAsIFIgbm90ZWJvb2spCiogW1VzaW5nIFIgYXMgR0lTXSgjZ2lzKSAocmVhZGluZyBhIHJhaW5mYWxsIHNoYXBlZmlsZSArIEtyaWdpbmcsIGBzZmAgKyBgbGVhZmxldGAgKyBgbWFwdmlld2AgKyBgT1NNc2NhbGVgKQoqIFtSaXZlciBkaXNjaGFyZ2UgdGltZS1zZXJpZXNdKCNkaXNjaGFyZ2UpIHZpc3VhbGlzYXRpb24gYW5kIGV4dHJlbWUgdmFsdWUgc3RhdGlzdGljcyAoYGFuaW1hdGlvbmAgKyBgZXh0cmVtZVN0YXRgKQoqIFtIeWRyb2xvZ2ljYWwgbW9kZWxsaW5nXSgjaHlkbW9kKSB3aXRoIGBhaXJHUmAKKiBbRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpc10oI2VkYSkgaW5jbHVkaW5nIGZsb3cgZHVyYXRpb24gY3VydmUgYW5kIHRyZW5kIGFuYWx5c2lzIG9uIHRpbWUtc2VyaWVzCgpcIAoKQmVmb3JlIHdlIGdldCBzdGFydGVkLCBwbGVhc2UgbGV0IHVzIGtub3cgeW91ciBjdXJyZW50IFIga25vd2xlZGdlIGxldmVsIGJ5IGZpbGxpbmcgb3V0IHRoZSBzaG9ydCBzdXJ2ZXkgYXQgIAo8Zm9udCBzaXplPSI2Ij5bYml0Lmx5L2tub3dSXShodHRwczovL2JpdC5seS9rbm93Uik8L2ZvbnQ+IAoKXAoKW3RvcF0oI3RvcCkKCiMgUmVwb3J0Ckdvb2QgY29kaW5nIHByYWN0aWNlLCByZXBvcnQgZ2VuZXJhdGlvbiAoUnN0dWRpbywgYHJtYXJrZG93bmAsIFIgbm90ZWJvb2spICAKKipEYW5pZWwgS2xvdHoqKgoKIyMjIFdoeSB0byB1c2UgUgoKIVtdKGRhbmllbC9pbnRyby9nb2Fscy5qcGVnKQoKV2h5IEkgZGlkIG5vdCB1c2UgUjogCgohW10oZGFuaWVsL2ludHJvL2VxdWFscy5qcGVnKQoKCldoYXRzIGdyZWF0IGFib3V0IFI6IApgYGB7cn0KICBsaWJyYXJ5KGdncGxvdDIpCiAgdGVzdF9kYXRhIDwtIG1wZwogIHRlc3RfcGxvdCA8LSBnZ3Bsb3QodGVzdF9kYXRhLCBhZXMoZGlzcGwsIGh3eSwgY29sb3VyID0gY2xhc3MpKSArIAogICAgZ2VvbV9wb2ludCgpCiAgdGVzdF9wbG90CmBgYAoKV2h5IEkgZGVjaWRlZCB0byB1c2UgUjogCgohW3BpcGVdKGRhbmllbC9pbnRyby9tYWdyaXR0ci5qcGVnKQoKUHJldmlvdXNseToKYGBge3J9CiAgYWdncmVnYXRpb25fZnVuY3Rpb24gPC0gZnVuY3Rpb24oeCkgewogICAgcm91bmQobWVhbih4KSwyKQogIH0KICBtdGNhcnNfc3Vic2V0IDwtIHN1YnNldChtdGNhcnMsaHAgPiAxMDApCiAgbXRjYXJzX2FnZ3JlZ2F0ZWQgPC0gYWdncmVnYXRlKC4gfiBjeWwsIGRhdGEgPSBtdGNhcnNfc3Vic2V0LCBGVU4gPSBhZ2dyZWdhdGlvbl9mdW5jdGlvbikKICBjYXJfZGF0YTEgPC0gdHJhbnNmb3JtKG10Y2Fyc19hZ2dyZWdhdGVkLCBrcGwgPSBtcGcqMC40MjUxKQogIHByaW50KGNhcl9kYXRhMSkKYGBgCgpOb3c6CmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpjYXJfZGF0YTIgPC0gCiAgbXRjYXJzICU+JQogIHN1YnNldChocCA+IDEwMCkgJT4lCiAgYWdncmVnYXRlKC4gfiBjeWwsIGRhdGEgPSAuLCBGVU4gPSAuICU+JSBtZWFuICU+JSByb3VuZCgyKSkgJT4lCiAgdHJhbnNmb3JtKGtwbCA9IG1wZyAlPiUgbXVsdGlwbHlfYnkoMC40MjUxKSkgJT4lCiAgcHJpbnQKYGBgCgoKKipidHc6KioKWW91IGNhbiBpbnRlZ3JhdGUgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIHdpdGggZWFzZS4gSGVyZSBhbiBleGFtcGxlIGZyb20gCltZaWh1aSBYaWVdKGh0dHBzOi8veWlodWkubmFtZS9rbml0ci9kZW1vL2VuZ2luZXMvKSBmb3IgdGhlIHVzZSBvZiBgRm9ydHJhbmAgCmluIFJtYXJrZG93bjoKCjEuIENvbXBpbGUgQ29kZToKICAgIGBgYHtyIGNvbXBmb3J0LCBlbmdpbmU9J2ZvcnRyYW4nLCByZXN1bHRzPSdoaWRlJywgZXZhbD1GQUxTRX0KICAgIEMgRm9ydHJhbiB0ZXN0CiAgICAgICAgICBzdWJyb3V0aW5lIGZleHAobiwgeCkKICAgICAgICAgIGRvdWJsZSBwcmVjaXNpb24geAogICAgQyAgb3V0cHV0CiAgICAgICAgICBpbnRlZ2VyIG4sIGkKICAgIEMgIGlucHV0IHZhbHVlCiAgICAgICAgICBkbyAxMCBpPTEsbgogICAgICAgICAgICAgeD1kZXhwKGRjb3MoZHNpbihkYmxlKGZsb2F0KGkpKSkpKQogICAgICAxMCAgY29udGludWUKICAgICAgICAgIHJldHVybgogICAgICAgICAgZW5kCiAgICBgYGAKCjIuIFJ1biBDb2RlOgogICAgYGBge3IgdGVzdGZvcnQsIGNvbGxhcHNlPVRSVUUsIGV2YWwgPSBGQUxTRX0KICAgIHJlcyA9IC5Gb3J0cmFuKCJmZXhwIiwgbj0xMDAwMDBMLCB4PTApCiAgICBzdHIocmVzKQogICAgYGBgCgpCZSBoYXBweSB3aXRoIHRoZSByZXN1bHQ6IAo+ICAgICBgIyMgTGlzdCBvZiAyYAo+ICAgICBgIyMgICQgbjogaW50IDEwMDAwMGAKPiAgICAgYCMjICAkIHg6IG51bSAyLjcyYAoKLS0tCgojIyMgTWFya2Rvd24gCkhUKipNKipMOiBIeXBlclRleHQgKipNYXJrZG93bioqIExhbmd1YWdlCgpKb2huIEdydWJlcjoKClshW0pvaG4gR3J1YmVyXShkYW5pZWwvaW50cm8vSm9obl9HcnViZXJfd2lraS5qcGVnKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSm9obl9HcnViZXIpCgoKQ29tcGFyaXNvbjogTWFya2Rvd24gdnMuIExhdGV4IApbIVtDb21wYXJpc29uXShkYW5pZWwvaW50cm8veWlodWlfbGF0ZXgtdnMtbWFya2Rvd24ucG5nKV0oaHR0cHM6Ly95b3V0dS5iZS8yeXZXME9fN3hPZykKCgpSc3R1ZGlvIHByb3ZpZGVzIGNoZWF0LXNoZWV0cyB3aXRoIHRoZSBtb3N0IGltcG9ydGFudCBpbmZvcm1hdGlvbnMgYWJvdXQgbWFueSAKb2YgdGhlaXIgImZhdm9yaXRlIiBwYWNrYWdlcyAmIHNvZnR3YXJlOgoKWyFbQ2hlYXQgU2hlZXRdKGRhbmllbC9pbnRyby9SbWFya2Rvd25fY2hlYXRzaGVldC5wbmcpXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKQoKKiogUm1hcmtkb3duICoqCgpJbiBSc3R1ZGlvOiAKCjxkaXYgYWxpZ249ImNlbnRlciI+CiAgPGltZyB3aWR0aD0iNjAwcHgiIHNyYz0iZGFuaWVsL3JlcG9ydHMvUm1hcmsxLnBuZyIgYWx0PSJSbWFyazEiIC8+CjwvZGl2PgoKPGRpdiBhbGlnbj0iY2VudGVyIj4KICA8aW1nIHdpZHRoPSI2MDBweCIgc3JjPSJkYW5pZWwvcmVwb3J0cy9SbWFyazIucG5nIiBhbHQ9IlJtYXJrMiIgLz4KPC9kaXY+Cgo8ZGl2IGFsaWduPSJjZW50ZXIiPgogIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9yZXBvcnRzL1JtYXJrMy5wbmciIGFsdD0iUm1hcmsyIiAvPgo8L2Rpdj4KCiJOYXRpdmUiIEZvcm1hdHM6CgotIFtodG1sXShkYW5pZWwvc2hvdy9SbWFyay5odG1sKSAKLSBbcGRmXShkYW5pZWwvc2hvdy9SbWFyay5wZGYpIAotIFt3b3JkXShkYW5pZWwvc2hvdy9SbWFyay5kb2NzKQoKTXVjaCBtb3JlIHBvc3NpYmxlIGlmIHlvdSBhZHJlc3MgcGFuZG9jIGRpcmVjdGx5OiAKWyFbcGFuZG9jXShkYW5pZWwvcmVwb3J0cy9wYW5kb2NfZGlhZ3JhbS5qcGcpXShodHRwOi8vcGFuZG9jLm9yZy8pCgpJbmZvcm1hdGlvbiBpbiB0aGUgdGV4dCBjYW4gYmUgYXV0b21hdGljYWxseSB1cGRhdGVkIHdpdGggdGhlIHJlc3Qgb2YgdGhlIApkb2N1bWVudDoKWyFbdGltZSBmb3IgY29mZmVlXShkYW5pZWwvcmVwb3J0cy9jb2ZmZWUucG5nKQoKIyMjIEV4YW1wbGVzCgojIyMjIFNtYWxsIFdlYnNpdGVzCjxkaXYgYWxpZ249ImNlbnRlciI+CiAgPGEgaHJlZiA9ICJodHRwOi8vcnN0dWRpby5naXRodWIuaW8vdHVmdGUvIj4KICAgIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9leGFtcGxlcy9zd190dWZ0ZS5wbmciIGFsdD0iQ2F5bWFuIFRoZW1lIiAvPgogIDwvYT4KPC9kaXY+Cgo8ZGl2IGFsaWduPSJjZW50ZXIiPgogIDxhIGhyZWYgPSAiaHR0cDovL3lpeHVhbi5jb3MubmFtZS9wcmV0dHlkb2MvY2F5bWFuLmh0bWwiPgogICAgPGltZyB3aWR0aD0iNjAwcHgiIHNyYz0iZGFuaWVsL2V4YW1wbGVzL3N3X2NheW1hbi5wbmciIGFsdD0iQ2F5bWFuIFRoZW1lIiAvPgogIDwvYT4KPC9kaXY+CgojIyMjIEJvb2tzIChibG9nZG93bikKWyFbYm9va2Rvd24xXShkYW5pZWwvZXhhbXBsZXMvYm9va2Rvd25fMS5wbmcpXShodHRwczovL2Jvb2tkb3duLm9yZy8pClshW2Jvb2tkb3duMl0oZGFuaWVsL2V4YW1wbGVzL2Jvb2tkb3duXzIucG5nKV0oaHR0cHM6Ly9ib29rZG93bi5vcmcvKQoKIyMjIyBCbG9ncyAoaHVnbykKWyFbaHVnb10oZGFuaWVsL2V4YW1wbGVzL2Jsb2dzX2h1Z28xLnBuZyldKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL2Jsb2dkb3duLykKCiMjIyMgUHJlc2VudGF0aW9ucwpbIVtib29rZG93bjFdKGRhbmllbC9leGFtcGxlcy9wcmVzZW50YXRpb25zXzEucG5nKV0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9yZXZlYWxqc19wcmVzZW50YXRpb25fZm9ybWF0Lmh0bWwpCgojIyMjIFdpZGdldHMgClshW2NyYW4tZ2F1Z2VdKGRhbmllbC9leGFtcGxlcy93aWRnZXRzXzEucG5nKV0oaHR0cHM6Ly9nYWxsZXJ5LnNoaW55YXBwcy5pby9jcmFuLWdhdWdlLykKCiMjIyMgQXBwcyAoU2hpbnkpIApbIVtzaGlueV0oZGFuaWVsL2V4YW1wbGVzL3NoaW55XzEuanBlZyldKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vYXV0aG9yaW5nX3NoaW55Lmh0bWwpClshW3NoaW55Ml0oZGFuaWVsL2V4YW1wbGVzL3NoaW55XzJfZ2FsbGVyeS5wbmcpXShodHRwczovL3NoaW55LnJzdHVkaW8uY29tL2dhbGxlcnkvc3VwZXJ6aXAtZXhhbXBsZS5odG1sKQohW3NoaW55Ml0oZGFuaWVsL2V4YW1wbGVzL3NoaW55XzNfam9oYW5uZXMucG5nKQoKClwKW3RvcF0oI3RvcCkKCiMgR0lTClVzaW5nIFIgYXMgR0lTIChyZWFkaW5nIGEgcmFpbmZhbGwgc2hhcGVmaWxlICsgS3JpZ2luZywgYHNmYCArIGBsZWFmbGV0YCArIGBtYXB2aWV3YCArIGBPU01zY2FsZWApICAKKipCZXJyeSBCb2Vzc2Vua29vbCoqCgojIyMgU2hhcGVmaWxlcwoKUmVhZGluZyBzaGFwZWZpbGVzIHdpdGggYG1hcHRvb2xzOjpyZWFkU2hhcGVTcGF0aWFsYCBhbmQgYHJnZGFsOjpyZWFkT0dSYCBpcyBvYnNvbGV0ZS4gIApJbnN0ZWFkLCB1c2UgYHNmOjpzdF9yZWFkYC4gYHNmYCBpcyBvbiBDUkFOIHNpbmNlIG9jdCAyMDE2LiAgCk1haW4gcmVhY3Rpb24gd2hlbiB1c2luZyBzZjogIldvdywgdGhhdCBpcyBmYXN0ISIgIApbRG93bmxvYWQgdGhlIHNoYXBlZmlsZV0oaHR0cHM6Ly9taW5oYXNrYW1hbC5naXRodWIuaW8vRG93bkdpdC8jL2hvbWU/dXJsPWh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRyby90cmVlL21hc3Rlci9wcmVzZW50YXRpb25zL2RhdGEvUHJlY0JyYW5kZW5idXJnKSAKb3IgYmV0dGVyOiBbZG93bmxvYWQgdGhlIHdob2xlIGdpdGh1YiBjb3Vyc2UgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvL2FyY2hpdmUvbWFzdGVyLnppcCkKCmBgYHtyfQpyYWluIDwtIHNmOjpzdF9yZWFkKCJkYXRhL1ByZWNCcmFuZGVuYnVyZy9uaWVkZXJzY2hsYWcuc2hwIikKYGBgCgpDZW50cmFsIHBvaW50cyBvZiByYWluZmFsbCBUaGllc3NlbiBwb2x5Z29ucwpgYGB7cn0KY2VudHJvaWRzIDwtIHNmOjpzdF9jZW50cm9pZChyYWluKQpjZW50cm9pZHMgPC0gc2Y6OnN0X2Nvb3JkaW5hdGVzKGNlbnRyb2lkcykKY2VudHJvaWRzIDwtIGFzLmRhdGEuZnJhbWUoY2VudHJvaWRzKQpgYGAKClt0b3BdKCN0b3ApCgojIyMgUGxvdHRpbmcsIG1hcHMKClN0YXRpYyBwbG90OgpgYGB7cn0KcGxvdChyYWluWywxXSkKYGBgCgpTdGF0aWMgbWFwOgpgYGB7cn0KcHJqIDwtIHNmOjpzdF9jcnMocmFpbikkcHJvajRzdHJpbmcKY2VudF9sbCA8LSBPU01zY2FsZTo6cHJvamVjdFBvaW50cyhZLFgsIGRhdGE9Y2VudHJvaWRzLCB0bz1PU01zY2FsZTo6cGxsKCksIGZyb209cHJqKQojbWFwX3N0YXRpYyA8LSBPU01zY2FsZTo6cG9pbnRzTWFwKHkseCwgY2VudF9sbCwgZng9MC4wOCwgdHlwZT0ibWFwdG9vbGtpdC10b3BvIiwgem9vbT02KQojc2F2ZShtYXBfc3RhdGljLCBmaWxlPSJkYXRhL21hcF9zdGF0aWMuUmRhdGEiKQpsb2FkKCJkYXRhL21hcF9zdGF0aWMuUmRhdGEiKQpPU01zY2FsZTo6cG9pbnRzTWFwKHkseCwgY2VudF9sbCwgbWFwPW1hcF9zdGF0aWMpCmBgYAoKSW50ZXJhY3RpdmUgbWFwOgpgYGB7cn0KbGlicmFyeShsZWFmbGV0KQpjZW50X2xsJGluZm8gPC0gcGFzdGUwKHNhbXBsZShsZXR0ZXJzLG5yb3coY2VudF9sbCksVFJVRSksICIsICIsIHJvdW5kKGNlbnRfbGwkeCwyKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIiwgIiwgcm91bmQoY2VudF9sbCR5LDIpKQpsZWFmbGV0KGNlbnRfbGwpICU+JSBhZGRUaWxlcygpICU+JSBhZGRDaXJjbGVNYXJrZXJzKGxuZz1+eCwgbGF0PX55LCBwb3B1cD1+aW5mbykKYGBgCgpJbnRlcmFjdGl2ZSBtYXAgb2Ygc2hhcGVmaWxlOgpgYGB7cn0KIyBtYWtlIHN1cmUgdG8gaGF2ZSBpbnN0YWxsZWQgdGhlIGRldmVsb3BtZW50IHZlcnNpb24gb2YgbWFwdmlldzogCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJlbnZpcm9ubWVudGFsaW5mb3JtYXRpY3MtbWFyYnVyZy9tYXB2aWV3IiwgcmVmID0gImRldmVsb3AiKQpsaWJyYXJ5KGJlcnJ5RnVuY3Rpb25zKSAjIGNsYXNzaWZ5LCBzZXFQYWwKY29sIDwtIHNlcVBhbChuPTEwMCwgY29sb3JzPWMoInJlZCIsInllbGxvdyIsImJsdWUiKSlbY2xhc3NpZnkocmFpbiRQMSkkaW5kZXhdCm1hcHZpZXc6Om1hcHZpZXcocmFpbiwgY29sLnJlZ2lvbnM9Y29sKQpgYGAKClt0b3BdKCN0b3ApCgojIyMgS3JpZ2luZwoKUGxvdCBvcmlnaW5hbCBwb2ludHMgY29sb3JlZCBieSB0aGlyZCBkaW1lbnNpb246CmBgYHtyfQpwY29sIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygicmVkIiwieWVsbG93IiwiYmx1ZSIpKSg1MCkKeCA8LSBjZW50cm9pZHMkWCAjIHVzZSBjZW50X2xsJHggZm9yIHByb2plY3RlZCBkYXRhCnkgPC0gY2VudHJvaWRzJFkKYmVycnlGdW5jdGlvbnM6OmNvbFBvaW50cyh4LCB5LCByYWluJFAxLCBhZGQ9RkFMU0UsIGNvbD1wY29sKQpgYGAKCkNhbGN1bGF0ZSB0aGUgdmFyaW9ncmFtIGFuZCBmaXQgYSBzZW1pdmFyaWFuY2UgY3VydmUKYGBge3J9CmxpYnJhcnkoZ2VvUikKZ2VvcHJlYyA8LSBhcy5nZW9kYXRhKGNiaW5kKHgseSxyYWluJFAxKSkKdmFyaW8gPC0gdmFyaW9nKGdlb3ByZWMsIG1heC5kaXN0PTEzMDAwMCkgIyBvdGhlciBtYXhkaXN0IGZvciBsYXQtbG9uIGRhdGEKZml0IDwtIHZhcmlvZml0KHZhcmlvKQpwbG90KHZhcmlvKQpsaW5lcyhmaXQpCmBgYAoKRGV0ZXJtaW5lIGEgdXNlZnVsIHJlc29sdXRpb24gCihrZWVwIGluIG1pbmQgdGhhdCBjb21wdXRpbmcgdGltZSByaXNlcyBleHBvbmVudGlhbGx5IHdpdGggZ3JpZCBzaXplKQpgYGB7cn0KIyBkaXN0YW5jZSB0byBjbG9zZXN0IG90aGVyIHBvaW50OgpkIDwtIHNhcHBseSgxOmxlbmd0aCh4KSwgZnVuY3Rpb24oaSkKICAgICAgICAgICAgbWluKGJlcnJ5RnVuY3Rpb25zOjpkaXN0YW5jZSh4W2ldLCB5W2ldLCB4Wy1pXSwgeVstaV0pKSApCiMgZm9yIGxhdC1sb25nIGRhdGEgdXNlICgyMDE3LUFwciBvbmx5IGF2YWlsYWJsZSBpbiBnaXRodWIgdmVyc2lvbiBvZiBPU01zY2FsZSkKIyBkIDwtIE9TTXNjYWxlOjptYXhFYXJ0aERpc3QoeSx4LCBkYXRhPWNlbnRfbGwsIGZ1bj1taW4pCmhpc3QoZC8xMDAwLCBicmVha3M9MjAsIG1haW49ImRpc3RhbmNlIHRvIGNsb3Nlc3QgZ2F1Z2UgW2ttXSIpCm1lYW4oZC8xMDAwKSAjIDgga20KYGBgCgpQZXJmb3JtIGtyaWdpbmcgb24gYSBncmlkIHdpdGggdGhhdCByZXNvbHV0aW9uIApgYGB7cn0KcmVzIDwtIDEwMDAgIyAxIGttLCBzaW5jZSBzdGF0aW9ucyBhcmUgOCBrbSBhcGFydCBvbiBhdmVyYWdlCmdyaWQgPC0gZXhwYW5kLmdyaWQoc2VxKG1pbih4KSxtYXgoeCkscmVzKSwKICAgICAgICAgICAgICAgICAgICBzZXEobWluKHkpLG1heCh5KSxyZXMpKQprcmljbyA8LSBrcmlnZS5jb250cm9sKHR5cGUua3JpZ2U9Ik9LIiwgb2JqLm1vZGVsPWZpdCkKI2tyb2JqIDwtIGtyaWdlLmNvbnYoZ2VvcHJlYywgbG9jPWdyaWQsIGtyaWdlPWtyaWNvKQojc2F2ZShrcm9iaiwgZmlsZT0iZGF0YS9rcm9iai5SZGF0YSIpCmxvYWQoImRhdGEva3JvYmouUmRhdGEiKSAjIGxpbmUgYWJvdmUgaXMgdG9vIHNsb3cgZm9yIHJlY3JlYXRpb24gZWFjaCB0aW1lCmBgYAoKUGxvdCB0aGUgaW50ZXJwb2xhdGVkIHZhbHVlcyB3aXRoIGBpbWFnZWAgb3IgYW4gZXF1aXZhbGVudCBmdW5jdGlvbiAKKHNlZSBbUmNsaWNrXShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yY2xpY2spIDQuMTUpIGFuZCBhZGQgY29udG91ciBsaW5lcy4KYGBge3J9CnBhcihtYXI9YygwLDMsMCwzKSkKZ2VvUjo6OmltYWdlLmtyaWdpbmcoa3JvYmosIGNvbD1wY29sKQpjb2xQb2ludHMoeCwgeSwgcmFpbiRQMSwgY29sPXBjb2wsIGxlZ2FyZ3M9bGlzdChob3Jpej1GLCB0aXRsZT0iUHJlYyIseTE9MC4xLHgxPTAuOSkpCnBvaW50cyh4LHkpCnBsb3QocmFpbiwgY29sPU5BLCBhZGQ9VFJVRSkKYGBgClwKW3RvcF0oI3RvcCkKCiMgRGlzY2hhcmdlClJpdmVyIGRpc2NoYXJnZSB0aW1lLXNlcmllcyB2aXN1YWxpc2F0aW9uIGFuZCBleHRyZW1lIHZhbHVlIHN0YXRpc3RpY3MgKGBhbmltYXRpb25gICsgYGV4dHJlbWVTdGF0YCkgIAoqKkJlcnJ5IEJvZXNzZW5rb29sKioKCiMjIyBSZWFkLCBwbG90IGFuZCBhZ2dyZWdhdGUgZGF0YQoKRGF0YXNldHMgZnJvbSB0aGUgVUsgTmF0aW9uYWwgUml2ZXIgRmxvdyBBcmNoaXZlCjxodHRwOi8vbnJmYS5jZWguYWMudWsvZGF0YS9zdGF0aW9uL21lYW5mbG93LzM5MDcyPiAgCkRvd25sb2FkIFtkaXNjaGFyZ2UgY3N2XShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8vdHJlZS9tYXN0ZXIvcHJlc2VudGF0aW9ucy9kYXRhL2Rpc2NoYXJnZTM5MDcyLmNzdikKb3IgYmV0dGVyOiBbZG93bmxvYWQgdGhlIHdob2xlIGdpdGh1YiBjb3Vyc2UgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvL2FyY2hpdmUvbWFzdGVyLnppcCkKClJlYWQgYW5kIHRyYW5zZm9ybSBkYXRhCmBgYHtyfQpRIDwtIHJlYWQudGFibGUoImRhdGEvZGlzY2hhcmdlMzkwNzIuY3N2Iiwgc2tpcD0xOSwgaGVhZGVyPVRSVUUsIHNlcD0iLCIsIGZpbGw9VFJVRSlbLDE6Ml0KY29sbmFtZXMoUSkgPC0gYygiZGF0ZSIsImRpc2NoYXJnZSIpClEkZGF0ZSA8LSBhcy5EYXRlKFEkZGF0ZSwgZm9ybWF0PSIlWS0lbS0lZCIpCmBgYAoKRXhhbWluZSBkYXRhCmBgYHtyfQpoZWFkKFEpCmBgYAoKYGBge3J9CnN0cihRKQpgYGAKClNpbXBsZSB0aW1lIHNlcmllcyBwbG90CmBgYHtyfQpwbG90KFEsIHR5cGU9ImwiLCBjb2w9ImJsdWUiKQpgYGAKClB1YmxpY2F0aW9uLXJlYWR5IGdyYXBoaWNzCmBgYHtyfQpwbmcoIkRpc2NoYXJnZVZpcy5wbmciLCB3aWR0aD0yMCwgaGVpZ2h0PTEwLCB1bml0cz0iY20iLCByZXM9NTAwKQojcGRmKCJEaXNjaGFyZ2VWaXMucGRmIiwgd2lkdGg9MjAvMi41LCBoZWlnaHQ9MTAvMi41KSAjIHZlY3RvciBncmFwaApwYXIobWFyPWMoMy41LDMuNSwyLjUsMC4yKSwgbWdwPWMoMi4zLDAuNywwKSwgbGFzPTEpCnBsb3QoUSwgdHlwZT0ibCIsIGNvbD0iYmx1ZSIsIG1haW49Ik5SRkE6IFRoYW1lc1xuUm95YWwgV2luZHNvciBQYXJrIiwKICAgICB4bGFiPSJEYXRlIiwgeWxhYj0iRGlzY2hhcmdlICBbbVxVezAwQjN9L3NdIikKZGV2Lm9mZigpCmBgYAoKQW5udWFsIG1heGltYSwgR2VybWFuIGh5ZHJvbG9naWNhbCB5ZWFyIHNwbGl0IGF0IE9jdCAzMQpgYGB7cn0KUSRoeWR5ZWFyIDwtIGFzLm51bWVyaWMoZm9ybWF0KFEkZGF0ZSs2MSwgIiVZIikpCmFubm1heCA8LSB0YXBwbHkoUSRkaXNjaGFyZ2UsIFEkaHlkeWVhciwgbWF4LCBuYS5ybT1UUlVFKQphbm5tYXggPC0gYW5ubWF4Wy0xXQpoeWR5ZWFyIDwtIGFzLm51bWVyaWMobmFtZXMoYW5ubWF4KSkgCnBsb3QoaHlkeWVhciwgYW5ubWF4LCB0eXBlPSJvIiwgbGFzPTEpCmBgYAoKIyMjIEV4dHJlbWUgdmFsdWUgc3RhdGlzdGljcwoKYGBge3J9CmxpYnJhcnkoZXh0cmVtZVN0YXQpCmRsZiA8LSBkaXN0TGZpdChhbm5tYXgpCnBsb3RMZml0KGRsZikKcGxvdExmaXQoZGxmLCBjZGY9VFJVRSkKZGxlIDwtIGRpc3RMZXh0cmVtZShkbGY9ZGxmLCBSUHM9Yyg1LDEwLDUwLDEwMCksIGdwZD1GQUxTRSkKcGxvdExleHRyZW1lKGRsZSkKYGBgCgpMb2dhcml0aG1pYyBwbG90IHdpdGggbWFueSBmaXR0ZWQgZGlzdHJpYnV0aW9uIGZ1bmN0aW9ucwpgYGB7cn0KcGxvdExleHRyZW1lKGRsZSwgbmJlc3Q9MTYsIGxvZz1UUlVFKQpgYGAKClJldHVybiB2YWx1ZXMgKGRpc2NoYXJnZSBlc3RpbWF0ZXMgZm9yIGdpdmVuIHJldHVybiBwZXJpb2RzKQpgYGB7cn0KZGxlJHJldHVybmxldgpgYGAKSW4gcmVhbGl0eSwgcGxlYXNlIHVzZSBub24tc3RhdGlvbmFyeSBFVlMhCgpVbmNlcnRhaW50eSBiYW5kIGZvciBXYWtlYnkgZGlzdHJpYnV0aW9uIGZpdCBlc3RpbWF0ZQpgYGB7cn0KZGxlX2Jvb3QgPC0gZGlzdExleEJvb3QoZGxlLCBuPTEwLCBuYmVzdD0xKQpwbG90TGV4Qm9vdChkbGVfYm9vdCwgZGlzdGNvbD0iZ3JlZW4iKQpgYGAKCgpNb3JlIGRldGFpbHMgaW4gdGhlIHBhY2thZ2UgdmlnbmV0dGUKYGBge3J9CnZpZ25ldHRlKCJleHRyZW1lU3RhdCIpCmBgYAoKCiMjIyBBbmltYXRlZCBtb3ZpZQoKRG93bmxvYWQgZGF0YSBmcm9tIHNldmVyYWwgZGlzY2hhcmdlIHN0YXRpb25zCgo8aHR0cDovL25yZmEuY2VoLmFjLnVrL2RhdGEvc2VhcmNoPiBGaWx0ZXI6IFJpdmVyID0gVGhhbWVzICAKPGh0dHA6Ly9lbnZpcm9ubWVudC5kYXRhLmdvdi51ay9mbG9vZC1tb25pdG9yaW5nL2RvYy9yZWZlcmVuY2Ujc3RhdGlvbnM+IGZvciBsYXQtbG9uIGNvb3JkaW5hdGVzOgoKYGBge3IsIGV2YWw9RkFMU0V9CnVybCA8LSAiaHR0cDovL2Vudmlyb25tZW50LmRhdGEuZ292LnVrL2Zsb29kLW1vbml0b3JpbmcvaWQvc3RhdGlvbnM/cml2ZXJOYW1lPVRoYW1lcyIKanNvbiA8LSBqc29ubGl0ZTo6ZnJvbUpTT04odXJsKQpzdHIoanNvbiRpdGVtcywgbWF4LmxldmVsPTEpCm1ldGFqc29uIDwtIGpzb24kaXRlbXNbLGMoImxhYmVsIiwibGF0IiwibG9uZyIsImRhdGVPcGVuZWQiKV0KYGBgCgpgYGB7cn0KbWV0YSA8LSByZWFkLnRhYmxlKGhlYWRlcj1ULCBhcy5pcz1ULCBzZXA9IiwiLCB0ZXh0PSIKbmFtZSAgICAgICAgICAgICAgICAsIGxhdCwgbG9uCktpbmdzdG9uICAgICAgICAgICAgLCA1MS40MTUwMDUsLTAuMzA4ODY5CkRheXNfV2VpciAgICAgICAgICAgLCA1MS42MzgyMDYsLTEuMTc5NDQ0CkV5bnNoYW0gICAgICAgICAgICAgLCA1MS43NzQ2OTIsLTEuMzU2ODU0Cldlc3RfTWlsbF9Dcmlja2xhZGUgLCA1MS42NDY2OTQsLTEuODY1NTM2CkV3ZW4gICAgICAgICAgICAgICAgLCA1MS42NzQ3MzMsLTEuOTkwNApSb3lhbF9XaW5kc29yX1BhcmsgICwgNTEuNDg1Nzk1LC0wLjU4OTEyNApSZWFkaW5nICAgICAgICAgICAgICwgNTEuNDYxMzI1LC0wLjk2Nzg4NAoiKQojbGlicmFyeShsZWFmbGV0KQojbGVhZmxldChtZXRhKSAlPiUgYWRkVGlsZXMoKSAlPiUgYWRkQ2lyY2xlTWFya2Vycyh+bG9uLCB+bGF0LCBwb3B1cD1+bmFtZSkKbWFwX3RoYW1lcyA8LSBPU01zY2FsZTo6cG9pbnRzTWFwKGxhdCxsb24sbWV0YSwgZng9MSwgZnk9MC4yLCBwbG90PUZBTFNFLCB6b29tPTYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPSJtYXB0b29sa2l0LXRvcG8iLCBxdWlldD1UUlVFKQpgYGAKClJlYWQgZGF0YXNldHMKCmBgYHtyfQpmaWxlcyA8LSBkaXIoImRhdGEiLCBwYXR0ZXJuPSJeVGhhbWVzXyIsIGZ1bGw9VFJVRSkKdGhhbWVzIDwtIGxhcHBseShmaWxlcywgZnVuY3Rpb24oZikgewogZGlzIDwtIHJlYWQudGFibGUoZiwgc2tpcD0xOSwgaGVhZGVyPVRSVUUsIHNlcD0iLCIsIGZpbGw9VFJVRSlbLDE6Ml0KIG5hbWUgPC0gcmVhZExpbmVzKGYsIG49NSlbNV0KIG5hbWUgPC0gc3ViKCJzdGF0aW9uLG5hbWUsVGhhbWVzIGF0ICIsICIiLCBuYW1lKQogbmFtZSA8LSBnc3ViKCIgIiwgIl8iLCBuYW1lKQogY29sbmFtZXMoZGlzKSA8LSBjKCJkYXRlIixuYW1lKQogZGlzJGRhdGUgPC0gYXMuRGF0ZShkaXMkZGF0ZSwgZm9ybWF0PSIlWS0lbS0lZCIpCiBkaXMKfSkKcm0oZmlsZXMpCmBgYAoKTWVyZ2UgZGF0YXNldHMKYGBge3J9CmRpcyA8LSBSZWR1Y2UoZnVuY3Rpb24oLi4uKSBtZXJnZSguLi4sIGFsbD1UKSwgdGhhbWVzKQpgYGAKCkNvZGUgdG8gZ2VuZXJhdGUgb25lIG1vdmllIHNsaWNlCmBgYHtyfQpsaWJyYXJ5KGJlcnJ5RnVuY3Rpb25zKSAjIGZvciBsaW0wLCBtb250aEF4aXMsIHRleHRGaWVsZCwgZXRjCgpzY2VuZSA8LSBmdW5jdGlvbihpLCB2bGNub3RlPVRSVUUsIG1hcD1UUlVFLCBjZXg9MS4yKQp7CiBzZWwgPC0gMDoxMjAKIGRpczIgPC0gZGlzW2kgKyBzZWwsIF0KIHN0YXQgPC0gY29sbmFtZXMoZGlzKVstMV0KIGNvbCA8LSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmNvbChkaXMpLTEsIG5hbWU9IlNldDEiKQogbmFtZXMoY29sKSA8LSBzdGF0CiBwbG90KGRpczIkZGF0ZSxkaXMyWywyXSwgdHlwZT0ibiIsIHlsaW09bGltMCgzMDApLCAgbGFzPTEsIAogICAgICB4YXh0PSJuIiwgeWF4dD0ibiIsIGNleC5sYWI9Y2V4LCB4bGFiPSIiLCAKICAgICAgeWxhYj0iRGlzY2hhcmdlICBbbVxVezAwQjN9L3NdIiwgeGF4cz0iaSIpCiBheGlzKDIsIGNleC5heGlzPWNleCwgbGFzPTEpCiBTeXMuc2V0bG9jYWxlKCJMQ19USU1FIiwgIkMiKQogbW9udGhBeGlzKG1pZG1vbnRoPVRSVUUsIGZvcm1hdD0iJWJcbiVZIiwgY2V4PWNleCwgbWdwPWMoMywzLjUsMCkpCiBhYmxpbmUoaD0xOjYqMTAwLCB2PWRpczIkZGF0ZVtmb3JtYXQoZGlzMiRkYXRlLCIlZCIpPT0iMDEiXSwgY29sPTgpCiBmb3IocyBpbiBzdGF0KSBsaW5lcyhkaXMyJGRhdGUsIGRpczJbLHNdLCBsd2Q9NCwgY29sPWNvbFtzXSkKIHhpIDwtIHNlcVIoc2VsLGxlbj1sZW5ndGgoc3RhdCkrMilbLTFdCiB4aSA8LSBoZWFkKHhpLCAtMSkKIHRleHRGaWVsZChkaXMyJGRhdGVbeGldLCBkaWFnKGFzLm1hdHJpeChkaXMyW3hpLHN0YXRdKSksIHN0YXQsIGNleD1jZXgsIGNvbD1jb2wpCiBib3goKQogaWYobWFwKSBiZXJyeUZ1bmN0aW9uczo6c21hbGxQbG90KAogICAgewogICAgT3BlblN0cmVldE1hcDo6cGxvdC5PcGVuU3RyZWV0TWFwKG1hcF90aGFtZXMsIHJlbW92ZU1hcmdpbj1GQUxTRSkKICAgIHB0cyA8LSBPU01zY2FsZTo6cHJvamVjdFBvaW50cyhsYXQsbG9uLG1ldGEsIAogICAgICAgICAgIHRvPW1hcF90aGFtZXMkdGlsZXNbWzFdXSRwcm9qZWN0aW9uKQogICBwb2ludHMocHRzLCBwY2g9MywgY2V4PTQsIGx3ZD01LCBjb2w9Y29sKQogICB9LAogICBtYXI9MCwgYmc9TkEsIGJvcmRlcj1OQSwgeDE9MCwgeDI9MC41LCB5MT0wLjgsIHkyPTEpCiBpZih2bGNub3RlKSBtdGV4dCgiVkxDOiAnZSc6IHNpbmdsZSBmcmFtZSBmb3J3YXJkXG4nU0hJRlQrTEVGVCc6IGZldyBzZWNvbmRzIGJhY2siLAogICAgICAgICAgICAgICAgICAgc2lkZT0zLCBsaW5lPS05LCBvdXRlcj1UUlVFLCBhZGo9MC45NSwgY2V4PWNleCkKfQoKcGFyKG1hcj1jKDUsNSwwLjUsMC41KSwgbWdwPWMoMywwLjcsMCkpCnNjZW5lKDQ3MjAwKQpgYGAKCkFjdHVhbCBtb3ZpZQpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShhbmltYXRpb24pIDsgbGlicmFyeShwYmFwcGx5KQpzYXZlVmlkZW8oe3BhcihtYXI9Yyg2LDgsMSwxKSwgbWdwPWMoNS41LDAuNywwKSkKIGR1bW15IDwtIHBic2FwcGx5KHNlcSg0NzAwMCwgYnk9MywgbGVuPTEwMCksIHNjZW5lLCBjZXg9Mik7IHJtKGR1bW15KQp9LCB2aWRlby5uYW1lPSJRbW92aWUubXA0IiwgaW50ZXJ2YWw9MC4xLCBmZm1wZWc9Ii91c3IvYmluL2ZmbXBlZyIsIAphbmkud2lkdGg9NyoyMDAsIGFuaS5oZWlnaHQ9NSoyMDApCmBgYAoKPGEgaHJlZj0iUW1vdmllLm1wNCI+T3BlbiB2aWRlbyB3aXRoaW4gYnJvd3NlcjwvYT4KClwKW3RvcF0oI3RvcCkKCiMgSHlkbW9kCkh5ZHJvbG9naWNhbCBtb2RlbGxpbmcgd2l0aCBgYWlyR1JgICAgCioqS2F0aWUgU21pdGgqKiAoQ2VudHJlIGZvciBFY29sb2d5ICYgSHlkcm9sb2d5KSA8ay5hLnNtaXRoQGNlaC5hYy51az4KClRoaXMgaXMgYW4gZGVtb25zdHJhdGlvbiBvZiBob3cgdG8gdXNlIHRoZSBhaXJHUiBwYWNrYWdlIG9mIGh5ZHJvbG9naWNhbCBtb2RlbHMgaW4gUiwgYXMgd2VsbCBhcyBob3cgdG8gcGxvdCBpbnRlcmFjdGl2ZSB0aW1lc2VyaWVzIGdyYXBocyB3aXRoIHRoZSBkeWdyYXBocyBwYWNrYWdlLgoKRmlyc3Qgd2UgbmVlZCB0byBsb2FkIHNvbWUgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoeHRzKQpsaWJyYXJ5KGR5Z3JhcGhzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgojIyMgRGF0YQoKTm93IHdlJ2xsIGxvYWQgaW4gc29tZSBvYnNlcnZhdGlvbmFsIGZsb3cgZGF0YSBmcm9tIHRoZSBSaXZlciBUaGFtZXMgKG5hdHVyYWxpc2VkKSBpbiBFbmdsYW5kIC0gd2l0aCB0aGFua3MgdG8gdGhlIE5hdGlvbmFsIFJpdmVyIEZsb3cgQXJjaGl2ZTogaHR0cDovL25yZmEuY2VoLmFjLnVrL2RhdGEvc2VhcmNoCmBgYHtyfQpvYnNlcnZlZF9kYXRhIDwtIHJlYWQuY3N2KCJkYXRhL1FvYnNfMzkwMDEwLmNzdiIpCm9ic2VydmVkX2RhdGEKb2JzZXJ2ZWRfZGF0YSREQVRFIDwtIHN0cnB0aW1lKG9ic2VydmVkX2RhdGEkREFURSwgZm9ybWF0ID0gIiVkLyVtLyVZIikKYGBgCgpJbiBvcmRlciB0byBwbG90IHRoaXMgYXMgYW4gaW50ZXJhY3RpdmUgZHlncmFwaCB3ZSBuZWVkIHRvIGNoYW5nZSBpdCB0byB4dHMgZm9ybWF0CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0Kb2JzZXJ2ZWRfZGF0YV94dHMgPC0gYXMueHRzKG9ic2VydmVkX2RhdGEkUW9icywgb3JkZXIuYnkgPSBvYnNlcnZlZF9kYXRhJERBVEUpCmR5Z3JhcGgob2JzZXJ2ZWRfZGF0YV94dHMsIG1haW49Ik5hdHVyYWxpc2VkIFN0cmVhbWZsb3cgT2JzZXJ2YXRpb25zIGZvciB0aGUgVGhhbWVzIGF0IEtpbmdzdG9uIiklPiUKICBkeUF4aXMoInkiLCBsYWJlbD0iU3RyZWFtZmxvdyAobTMvcyIpJT4lCiAgZHlPcHRpb25zKGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgzLCAiU2V0MSIpWzJdKSU+JQogIGR5UmFuZ2VTZWxlY3RvcigpCmBgYApOb3cgbGV0cyByZWFkIGluIHNvbWUgcHJlY2lwaXRhdGlvbiBkYXRhIC0gdGhpcyBpcyBmcm9tIENFSC1HRUFSOiBodHRwczovL2RhdGEuZ292LnVrL2RhdGFzZXQvZ3JpZGRlZC1lc3RpbWF0ZXMtb2YtZGFpbHktYW5kLW1vbnRobHktYXJlYWwtcmFpbmZhbGwtZm9yLXRoZS11bml0ZWQta2luZ2RvbS0xODkwLTIwMTItY2VoLWdlYXIKCmBgYHtyfQpwcmVjaXBfZGF0YSA8LSByZWFkLmNzdigiZGF0YS9yYWluXzE5NjFfMjAxNF8zOTAwMTAuY3N2IikKcHJlY2lwX2RhdGEKYGBgCmFuZCBzb21lIHBvdGVudGlhbCBldmFwb3RyYW5zcGlyYXRpb24gZGF0YSAtIHRoaXMgaXMgZnJvbSBDSEVTUy1QRTpodHRwczovL2RhdGEuZ292LnVrL2RhdGFzZXQvY2xpbWF0ZS1oeWRyb2xvZ3ktYW5kLWVjb2xvZ3ktcmVzZWFyY2gtc3VwcG9ydC1zeXN0ZW0tcG90ZW50aWFsLWV2YXBvdHJhbnNwaXJhdGlvbi1kYXRhc2V0LWZvci0xCgpgYGB7cn0KUEVUX2RhdGEgPC0gcmVhZC5jc3YoImRhdGEvQ0hFU1NfUEVUXzE5NjFfMjAxNV8zOTAwMTAuY3N2IikKUEVUX2RhdGEKYGBgCgpOb3RlIHRoYXQgb3VyIHN0YXJ0aW5nIGRhdGVzIGFyZSBub3QgdGhlIHNhbWUgYXMgb3VyIG9ic2VydmF0aW9uYWwgZGF0YSwgc28gd2UgbmVlZCB0byBtYWtlIGEgZGF0YWZyYW1lIHRoYXQgbWF0Y2hlcyB0aGUgZGF0ZXMgdXAuIFRoZXJlIGFyZSBhIGxvdCBvZiB3YXlzIHRvIGRvIHRoaXMuIEZpcnN0IHdlJ2xsIGNvbnZlcnQgdGhlbSB0byB0aGUgc2FtZSBkYXRlIGZvcm1hdC4KYGBge3J9CnByZWNpcF9kYXRhJERBVEUgPC0gc3RycHRpbWUocHJlY2lwX2RhdGEkREFURSwgIiVZLSVtLSVkIikKUEVUX2RhdGEkREFURSA8LSBzdHJwdGltZShQRVRfZGF0YSREQVRFLCAiJVktJW0tJWQiKQpgYGAKbm93IHdlJ2xsIGZpbmQgdGhlIGNvbW1vbiBwZXJpb2QKYGBge3J9CmZpcnN0X2RhdGUgPC0gbWF4KG9ic2VydmVkX2RhdGEkREFURVsxXSwgcHJlY2lwX2RhdGEkREFURVsxXSwgUEVUX2RhdGEkREFURVsxXSkKbGFzdF9kYXRlIDwtIG1pbihvYnNlcnZlZF9kYXRhJERBVEVbbGVuZ3RoKG9ic2VydmVkX2RhdGEkREFURSldLCBwcmVjaXBfZGF0YSREQVRFW2xlbmd0aChwcmVjaXBfZGF0YSREQVRFKV0sIFBFVF9kYXRhJERBVEVbbGVuZ3RoKFBFVF9kYXRhJERBVEUpXSkKYGBgCmFuZCBtYWtlIGEgZGF0YSBmcmFtZSBvZiB0aGF0IGxlbmd0aApgYGB7cn0KIyBtYWtlIGFuIGVtcHR5IGRhdGEgZnJhbWUKdGhhbWVzX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgoTkEsbnJvdz1hcy5udW1lcmljKChsYXN0X2RhdGUtZmlyc3RfZGF0ZSkrMSksIG5jb2w9NCkpCmNvbG5hbWVzKHRoYW1lc19kYXRhKSA8LWMgKCJkYXRlIiwiUEVUIiwicHJlY2lwIiwib2JzIikKIyBtYWtlIHRoZSBkYXRlIHRpbWVzZXJpZXMKdGhhbWVzX2RhdGEkZGF0ZSA8LSBzZXEoZmlyc3RfZGF0ZSwgbGFzdF9kYXRlLCBieT0iZGF5cyIpCiMgcG9wdWxhdGUgdGhlIGRhdGEgZnJhbWUgd2l0aCB0aGUgZGF0YQp0aGFtZXNfZGF0YSRvYnMgPC0gb2JzZXJ2ZWRfZGF0YSRRb2JzW3doaWNoKG9ic2VydmVkX2RhdGEkREFURT09dGhhbWVzX2RhdGEkZGF0ZVsxXSk6d2hpY2gob2JzZXJ2ZWRfZGF0YSREQVRFPT10aGFtZXNfZGF0YSRkYXRlW2xlbmd0aCh0aGFtZXNfZGF0YSRkYXRlKV0pXQp0aGFtZXNfZGF0YSRwcmVjaXAgPC0gcHJlY2lwX2RhdGEkTWVhbl9yYWluZmFsbFt3aGljaChwcmVjaXBfZGF0YSREQVRFPT10aGFtZXNfZGF0YSRkYXRlWzFdKTp3aGljaChwcmVjaXBfZGF0YSREQVRFPT10aGFtZXNfZGF0YSRkYXRlW2xlbmd0aCh0aGFtZXNfZGF0YSRkYXRlKV0pXQp0aGFtZXNfZGF0YSRQRVQgPC0gUEVUX2RhdGEkUEVUW3doaWNoKFBFVF9kYXRhJERBVEU9PXRoYW1lc19kYXRhJGRhdGVbMV0pOndoaWNoKFBFVF9kYXRhJERBVEU9PXRoYW1lc19kYXRhJGRhdGVbbGVuZ3RoKHRoYW1lc19kYXRhJGRhdGUpXSldCmBgYAoKIyMjIEludGVyYWN0aXZlIHRpbWUgc2VyaWVzIHBsb3QKCnBsb3QgdGhlIG9ic2VydmVkIHN0cmVhbWZsb3cgd2l0aCB0aGUgcHJlY2lwaXRhdGlvbiBkYXRhCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0KIyAgY29udmVydCB0aGUgb2JzZXJ2ZWQgZGlzY2hhcmdlIHRvIHJ1bm9mZiAoc28gaXRzIGluIHRoZSBzYW1lIHVuaXRzIGFzIHRoZSBwcmVjaXApCiMgZGl2aWRlIGJ5IGNhdGNobWVudCBhcmVhIChtMikgYW5kIG11bGl0cGx5IGJ5IDg2LjQKdGhhbWVzX2RhdGEkb2JzIDwtICh0aGFtZXNfZGF0YSRvYnMvOTk0OC4wKSo4Ni40CnRoYW1lc19kYXRhX3h0cyA8LSBhcy54dHModGhhbWVzX2RhdGFbLDM6NF0sIG9yZGVyLmJ5PXRoYW1lc19kYXRhJGRhdGUpCiMgaW5pdGlhdGUgdGhlIGR5Z3JhcGgKZHlncmFwaCh0aGFtZXNfZGF0YV94dHMpJT4lCiMgZGVmaW5lIHRoZSBmaXJzdCBheGlzICAKZHlBeGlzKCJvYnMiLCBuYW1lID0gInkiLCBsYWJlbCA9ICJydW5vZmYgKG1tL2RheSkiLAogICAgICAgdmFsdWVSYW5nZSA9IHJhbmdlKHRoYW1lc19kYXRhX3h0c1ssICJvYnMiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKiBjKDAuMDEsIDEuNTkpKSU+JQojIGRlZmluZSB0aGUgc2Vjb25kIGF4aXMKZHlBeGlzKCJwcmVjaXAiLCBuYW1lID0gInkyIiwgbGFiZWwgPSAicHJlY2lwIChtbS9kYXkpIiwKICAgICAgICAgICAgICAgICAgIHZhbHVlUmFuZ2UgPSByZXYocmFuZ2UodGhhbWVzX2RhdGFfeHRzWywgInByZWNpcCJdLCAKICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkqIGMoMC4wMSwgMi45OSkpKSU+JQojIHBsb3QgdGhlIGRhdGEKZHlTZXJpZXMoIm9icyIsYXhpcyA9ICd5JyklPiUKZHlTZXJpZXMoInByZWNpcCIsIGF4aXMgPSAneTInLCBzdGVwUGxvdCA9IFRSVUUsCiAgICAgICAgIGZpbGxHcmFwaCA9IFRSVUUpJT4lCmR5T3B0aW9ucyhjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMywgIlNldDEiKVsyOjNdKSU+JQpkeVJhbmdlU2VsZWN0b3IoKQpgYGAKCiMjIyBIeWRyb2xvZ2ljYWwgbW9kZWxpbmcKCk9LLCBlbm91Z2ggbWVzc2luZyB3aXRoIGRhdGEsIGxldHMgZG8gc29tZSBtb2RlbGxpbmcKCnNlZSB0aGlzIHdlYnNpdGUgZm9yIGEgZ29vZCBndWlkZSB0aHJvdWdoIHRoZSBtb2RlbDogCjxodHRwczovL29kZWxhaWd1ZS5naXRodWIuaW8vYWlyR1IvdHV0b3JpYWxfMV9nZXR0aW5nX3N0YXJ0ZWQuaHRtbD4KCmxvYWQgdGhlIEdSIHBhY2thZ2UKYGBge3J9CnJlcXVpcmUoYWlyR1IsIHF1aWV0bHk9VFJVRSkKYGBgCgpwcmVwYXJlIHRoZSBpbnB1dCBkYXRhIGluIHRoZSBjb3JyZWN0IGZvcm1hdApgYGB7cn0KQmFzaW5PYnMgPC0gdGhhbWVzX2RhdGEKY29sbmFtZXMoQmFzaW5PYnMpIDwtIGMoJ0RhdGVzUicsJ0UnLCdQJywgJ1FvYnMnKQpgYGAKCmNyZWF0ZSB0aGUgSW5wdXRzTW9kZWwgb2JqZWN0IC0gdGhpcyBkZWZpbmVzIHdoaWNoIG1vZGVsIHdlIHdhbnQgdG8gcnVuLCBhbmQgZGVmaW5lcyB0aGUgdmFyaWFibGVzIGZvciB0aGUgbW9kZWxzIGlucHV0IGRhdGEKCmBgYHtyfQpJbnB1dHNNb2RlbCA8LSBDcmVhdGVJbnB1dHNNb2RlbChGVU5fTU9EID0gUnVuTW9kZWxfR1I0SixEYXRlc1IgPSBCYXNpbk9icyREYXRlc1IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJlY2lwID0gQmFzaW5PYnMkUCxQb3RFdmFwID0gQmFzaW5PYnMkRSkKc3RyKElucHV0c01vZGVsKQojIG5vdGUgTkEgdmFsdWVzIG9mIHByZWNpcCBhbmQgUEVUIGFyZSBOT1QgQUxMT1dFRApgYGAKCmNyZWF0ZSB0aGUgUnVuT3B0aW9ucyBvYmplY3QgLSB0aGlzIGRlZmluZXMgb3B0aW9ucyBmb3IgdGhlIFJ1bk1vZGVsX0dSNEogZnVuY3Rpb24KYGBge3J9CiMgZmlyc3QgZGVmaW5lIHRoZSBwZXJpb2QgdG8gcnVuIHRoZSBtb2RlbCBvdmVyCkluZF9SdW4gPC0gc2VxKHdoaWNoKEJhc2luT2JzJERhdGVzUj09IjE5ODEtMDEtMDEiKSwKICAgICAgICAgICAgIHdoaWNoKEJhc2luT2JzJERhdGVzUj09IjIwMTQtMTItMzEiKSkKUnVuT3B0aW9ucyA8LSBDcmVhdGVSdW5PcHRpb25zKEZVTl9NT0QgPSBSdW5Nb2RlbF9HUjRKLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluZFBlcmlvZF9SdW4gPSBJbmRfUnVuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5kUGVyaW9kX1dhcm1VcCA9IE5VTEwpCnN0cihSdW5PcHRpb25zKQpgYGAKCmNyZWF0ZSB0aGUgSW5wdXRzQ3JpdCBvYmplY3QgLSBkZWZpbmUgdGhlIGVycm9yIG1ldHJpYyAoY2hvb3NlIGZyb20gUk1TRSwgTlNFLCBLR0Ugb3IgbW9kaWZpZWQgS0dFIChLR0UyKSkKYGBge3J9CklucHV0c0NyaXQgPC0gQ3JlYXRlSW5wdXRzQ3JpdChGVU5fQ1JJVCA9IEVycm9yQ3JpdF9OU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuT3B0aW9ucyA9IFJ1bk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRb2JzID0gQmFzaW5PYnMkUW9ic1tJbmRfUnVuXSkKc3RyKElucHV0c0NyaXQpCmBgYAoKY3JlYXRlIHRoZSBDYWxpYk9wdGlvbnMgb2JqZWN0IC0gY2hvb3NlIHRoZSBjYWxpYnJhdGlvbiBhbGdvcml0aG0KYGBge3J9CkNhbGliT3B0aW9ucyA8LSBDcmVhdGVDYWxpYk9wdGlvbnMoRlVOX01PRCA9IFJ1bk1vZGVsX0dSNEosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOX0NBTElCID0gQ2FsaWJyYXRpb25fTWljaGVsKQpzdHIoQ2FsaWJPcHRpb25zKQpgYGAKCnJ1biB0aGUgY2FsaWJyYXRpb24KYGBge3J9Ck91dHB1dHNDYWxpYiA8LSBDYWxpYnJhdGlvbl9NaWNoZWwoSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuT3B0aW9ucyA9IFJ1bk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzQ3JpdCA9IElucHV0c0NyaXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2FsaWJPcHRpb25zID0gQ2FsaWJPcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTl9NT0QgPSBSdW5Nb2RlbF9HUjRKLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTl9DUklUID0gRXJyb3JDcml0X05TRSkKYGBgCgpOU0Ugb2YgMC45MjQ2IC0gbm90IGJhZCBhdCBhbGwhCgpkZWZpbmUgdGhlIHBhcmFtZXRlcnMgZm91bmQgYnkgdGhlIGNhbGlicmF0aW9uIHJvdXRpbmUKYGBge3J9ClBhcmFtIDwtIE91dHB1dHNDYWxpYiRQYXJhbUZpbmFsUgpQYXJhbQpgYGAKCioqUlVOIFRIRSBNT0RFTCEqKgpgYGB7cn0KT3V0cHV0c01vZGVsIDwtIFJ1bk1vZGVsX0dSNEooSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQYXJhbT0gUGFyYW0pCnN0cihPdXRwdXRzTW9kZWwpCmBgYAoKdXNlIHRoZSBpbmJ1aWx0IHBsb3QgZnVuY3Rpb24gdG8gbG9vayBhdCB0aGUgcmVzdWx0cwpgYGB7cn0KcGxvdChPdXRwdXRzTW9kZWwsIFFvYnM9QmFzaW5PYnMkUW9ic1tJbmRfUnVuXSkKYGBgCgpsb29raW5nIGdvb2QgLSBidXQgd2UndmUgZ290IHNvbWUgZGlzY3JlcGFuY3kgYXQgdGhlIGxvdyBmbG93cyBlbmQuIE5TRSBpcyBub3RvcmlvdXMgZm9yIHRoaXMsIGl0IGlzIGJhc2VkIG9uIHRoZSBzcXVhcmUgb2YgdGhlIGZsb3dzLCBzbyBvdmVyLXdlaWdodHMgdGhlIGNhbGlicmF0aW9uIHRvIHRoZSBoaWdoIGZsb3dzLiBJIHdvbmRlciBpZiB0aGUgbW9kaWZpZWQgS0dFIGNhbiBkbyBhbnkgYmV0dGVyPwpgYGB7cn0KIyBtYWtlIGEgZmV3IGNoYW5nZXMgdG8gdGhlIGNhbGlicmF0aW9uIGNyaXRlcmlhCklucHV0c0NyaXQgPC0gQ3JlYXRlSW5wdXRzQ3JpdChGVU5fQ1JJVCA9IEVycm9yQ3JpdF9LR0UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ1bk9wdGlvbnMgPSBSdW5PcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUW9icyA9IEJhc2luT2JzJFFvYnNbSW5kX1J1bl0pCiMgcmVydW4gdGhlIGNhbGlicmF0aW9uCk91dHB1dHNDYWxpYiA8LSBDYWxpYnJhdGlvbl9NaWNoZWwoSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuT3B0aW9ucyA9IFJ1bk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzQ3JpdCA9IElucHV0c0NyaXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2FsaWJPcHRpb25zID0gQ2FsaWJPcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTl9NT0QgPSBSdW5Nb2RlbF9HUjRKLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTl9DUklUID0gRXJyb3JDcml0X0tHRTIpCiMgcmVkZWZpbmUgdGhlIHBhcmFtZXRlcnMKUGFyYW0gPC0gT3V0cHV0c0NhbGliJFBhcmFtRmluYWxSCiMgcmVydW4gdGhlIG1vZGVsCk91dHB1dHNNb2RlbCA8LSBSdW5Nb2RlbF9HUjRKKElucHV0c01vZGVsID0gSW5wdXRzTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuT3B0aW9ucyA9IFJ1bk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGFyYW09IFBhcmFtKQojIHBsb3QgYWdhaW4KcGxvdChPdXRwdXRzTW9kZWwsIFFvYnM9QmFzaW5PYnMkUW9ic1tJbmRfUnVuXSkKYGBgCgpub3QgbXVjaCBkaWZmZXJlbnQuIE9oIHdlbGwsIHdlIGNhbiBiZSBoYXBweSB3aXRoIGVpdGhlciBvZiB0aG9zZSBtZXRyaWMgc2NvcmVzLiAtIHBhdXNlIGZvciB0aG91Z2h0IC0gd2hpY2ggcGFyYW1ldGVyIHNldCB3b3VsZCB5b3UgY2hvb3NlIHRvIHVzZT8hCgojIyMgVmFsaWRhdGlvbgoKTGV0J3MgZG8gc29tZSB2YWxpZGF0aW9uCmBgYHtyfQojIGdvIGJhY2sgdG8gdGhlIGJlZ2lubmluZywgcmVkZWZpbmUgdGhlIHBlcmlvZCB0byBydW4gb24gKHRoZSBwZXJpb2Qgd2UgaGF2ZW4ndCB1c2VkIGZvciBjYWxpYnJhdGlvbiwgbWludXMgdGhlIGZpcnN0IHllYXIgbmVlZGVkIGZvciB3YXJtIHVwKQpJbmRfUnVuIDwtIHNlcSh3aGljaChCYXNpbk9icyREYXRlc1I9PSIxOTYyLTAxLTAxIiksCiAgICAgICAgICAgICB3aGljaChCYXNpbk9icyREYXRlc1I9PSIxOTgwLTEyLTMxIikpClJ1bk9wdGlvbnMgPC0gQ3JlYXRlUnVuT3B0aW9ucyhGVU5fTU9EID0gUnVuTW9kZWxfR1I0SiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElucHV0c01vZGVsID0gSW5wdXRzTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmRQZXJpb2RfUnVuID0gSW5kX1J1biwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluZFBlcmlvZF9XYXJtVXAgPSBOVUxMKQpJbnB1dHNDcml0IDwtIENyZWF0ZUlucHV0c0NyaXQoRlVOX0NSSVQgPSBFcnJvckNyaXRfS0dFMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElucHV0c01vZGVsID0gSW5wdXRzTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFFvYnMgPSBCYXNpbk9icyRRb2JzW0luZF9SdW5dKQpQYXJhbSA8LSBPdXRwdXRzQ2FsaWIkUGFyYW1GaW5hbFIKT3V0cHV0c01vZGVsIDwtIFJ1bk1vZGVsX0dSNEooSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQYXJhbT0gUGFyYW0pCk91dHB1dHNDcml0IDwtIEVycm9yQ3JpdF9LR0UyKElucHV0c0NyaXQgPSBJbnB1dHNDcml0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3V0cHV0c01vZGVsID0gT3V0cHV0c01vZGVsKQpgYGAKCnNsaWdodGx5IHdvcnNlIHRoYW4gdGhlIGNhbGlicmF0aW9uIHBlcmlvZCAoMC45NjIxKSBidXQgbm90IGJhZCBhdCBhbGwKCmZpbmFsbHksIGxldCdzIHJ1biB0aGUgbW9kZWwgZm9yIHRoZSB3aG9sZSB0aW1lIHBlcmlvZCBhbmQgcGxvdCBhIGR5Z3JhcGggc28gd2UgY2FuIGxvb2sgYXQgdGhlIHRpbWVzZXJpZXMgaW4gbW9yZSBkZXRhaWwKYGBge3J9CkluZF9SdW4gPC0gc2VxKHdoaWNoKEJhc2luT2JzJERhdGVzUj09IjE5NjItMDEtMDEiKSwKICAgICAgICAgICAgIHdoaWNoKEJhc2luT2JzJERhdGVzUj09IjIwMTQtMTItMzEiKSkKUnVuT3B0aW9ucyA8LSBDcmVhdGVSdW5PcHRpb25zKEZVTl9NT0QgPSBSdW5Nb2RlbF9HUjRKLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluZFBlcmlvZF9SdW4gPSBJbmRfUnVuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5kUGVyaW9kX1dhcm1VcCA9IE5VTEwpClBhcmFtIDwtIE91dHB1dHNDYWxpYiRQYXJhbUZpbmFsUgpPdXRwdXRzTW9kZWwgPC0gUnVuTW9kZWxfR1I0SihJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ1bk9wdGlvbnMgPSBSdW5PcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBhcmFtPSBQYXJhbSkKcGxvdF9vdXRwdXRfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChOQSwgbmNvbCA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IGxlbmd0aChPdXRwdXRzTW9kZWwkRGF0ZXNSKSkpCmNvbG5hbWVzKHBsb3Rfb3V0cHV0X2RhdGEpIDwtIGMoIkRhdGUiLCAiUXNpbSIsICJRb2JzIikKcGxvdF9vdXRwdXRfZGF0YSREYXRlIDwtIE91dHB1dHNNb2RlbCREYXRlc1IKcGxvdF9vdXRwdXRfZGF0YSRRc2ltIDwtIE91dHB1dHNNb2RlbCRRc2ltCnBsb3Rfb3V0cHV0X2RhdGEkUW9icyA8LSBCYXNpbk9icyRRb2JzW0luZF9SdW5dCnBsb3Rfb3V0cHV0X2RhdGFfeHRzIDwtIGFzLnh0cyhwbG90X291dHB1dF9kYXRhLCBvcmRlci5ieSA9IHBsb3Rfb3V0cHV0X2RhdGEkRGF0ZSkKZHlncmFwaChwbG90X291dHB1dF9kYXRhX3h0cywgbWFpbj0iT2JzZXJ2ZWQgYW5kIFNpbXVsYXRlZCBSdW5vZmYgZm9yIHRoZSBUaGFtZXMgYXQgS2luZ3N0b24gKE5hdHVyYWxpc2VkKSIpJT4lCiAgZHlPcHRpb25zKGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgzLCJTZXQxIilbMjoxXSklPiUKICBkeUF4aXMoInkiLCBsYWJlbD0iUnVub2ZmIChtbS9kYXkpIiklPiUKICBkeVJhbmdlU2VsZWN0b3IoKQpgYGAKClRoYW5rcyBmb3IgbGlzdGVuaW5nISBIb3BlIHlvdSBnZXQgdG8gdHJ5IGl0IG91dC4KCkFueSBnZW5lcmFsIHF1ZXN0aW9ucz8gUGxlYXNlIGZlZWwgZnJlZSB0byBwb3N0IHRoZW0gb24gZmFjZWJvb2sgcGFnZTogaHR0cHM6Ly93d3cuZmFjZWJvb2suY29tL2dyb3Vwcy8xMTMwMjE0Nzc3MTIzOTA5Lz9yZWY9Ym9va21hcmtzCgpHUiBzcGVjaWZpYyBxdWVzdGlvbnM/IEVtYWlsIDxhaXJHUkBpcnN0ZWEuZnI+CgpcClt0b3BdKCN0b3ApCgojIEVEQQpFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIGluY2x1ZGluZyBmbG93IGR1cmF0aW9uIGN1cnZlIGFuZCB0cmVuZCBhbmFseXNpcyBvbiB0aW1lLXNlcmllcyAgIAoqKlNoYXVuIEhhcnJpZ2FuKioKClwKW3RvcF0oI3RvcCkKCgojIERpc2N1c3Npb24KClBsZWFzZSBnaXZlIHVzIGZlZWRiYWNrIGF0Cjxmb250IHNpemU9IjYiPltiaXQubHkvZmVlZGJhY2tSXShodHRwczovL2JpdC5seS9mZWVkYmFja1IpPC9mb250PiAKCkZvciBkaXNjdXNzaW9ucywgcGxlYXNlIHVzZSB0aGUgCltIeWRyb2xvZ3kgaW4gUiBGYWNlYm9vayBncm91cF0oaHR0cHM6Ly93d3cuZmFjZWJvb2suY29tL2dyb3Vwcy8xMTMwMjE0Nzc3MTIzOTA5LykuICAKCg==